/*
    共享数据，造成数据不一致问题。和多线程并发访问是有关系的。
    对一个全局变量进行多线程并发--/++操作是否是安全的？不安全 --- 怎么解决？
    对共享数据的访问，保证任何时候只有一个执行流！！--> 互斥(使用互斥锁--对代码进行加锁) 
    定义锁有两种方案：1、定义成全局的，用宏初始化，不需要释放。   2、定义成非全局的，要释放。
    加锁的本质：用时间来换安全性。
    加锁的表现：线程对于临界区代码串行执行。
    加锁原则：尽量保证临界区代码越少越好！

    在加锁和解锁之间的区域，叫做临界区。
    纯互斥环境，如果锁分配不够合理，容易导致其他线程的饥饿问题。不是说只要有互斥，必有饥饿。

    规则：1、外面来的，必须排队。 2、出来的，不能立马重新申请锁，必须排列到队列的尾部。。--> 让所有的线程获取锁由无序的变成有序的。

    按照一定的顺序性，获取资源，叫做同步！

    锁本身就是共享资源！因为每个线程都要申请。谁保证锁的安全？申请锁和释放锁在设计上本身就是原子性操作。

    tickets--不是原子的。会变成3条汇编语句。原子：一条汇编就是原子的！

    锁的原理：
        lock:   
            movb $0 %al
            xchgb %al, mutex
            if(al寄存器的内容 > 0)
            {
                return 0;
            }
            else 
            {
                挂起等待;
            }
            goto lock;
        unlock:
            movb $1, mutex
            唤醒等待Mutex的线程;
            return 0;
*/
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <vector>

using namespace std;
#define NUM 4

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

int tickets = 5000; // 多线程模拟一轮抢票

class threadData
{
public:
    // threadData(int number, pthread_mutex_t* mutex)
    threadData(int number)
    {
        _thread_name = "thread-" + to_string(number);
        // _lock = mutex;
    }

    string _thread_name;

    // 有了全局的锁就不要这把锁了
    // pthread_mutex_t* _lock;
};

void* getTicket(void* args)
{
    threadData* td = static_cast<threadData*>(args);
    const char* name = td->_thread_name.c_str();
    while(true)
    {
        // 线程对于锁的竞争能力可能会不同。
        // pthread_mutex_lock(td->_lock); // 申请锁成功，才能往后执行。不成功，阻塞等待。
        pthread_mutex_lock(&lock); 

        // 在临界区，线程可以被切换吗？可以。在被切换出去的时候，是持有锁被切走的。
        // 对于外边的线程，一个线程要么没有锁，要么释放锁。当前线程访问临界区的过程，对其他线程是原子的。
        if(tickets > 0)
        {
            usleep(1000); // 模拟抢票的时间
            printf("who=%s, get a ticket: %d\n", name, tickets);
            tickets--;
            // pthread_mutex_unlock(td->_lock);
            pthread_mutex_unlock(&lock);
        }
        else{
            // pthread_mutex_unlock(td->_lock);
            pthread_mutex_unlock(&lock);
            break;
        }
        usleep(15); // 这行代码让释放的锁别立即申请，同时其他线程也是有更多机会竞争该锁的。其实多线程还要执行得到票之后的后续动作。
        // pthread_mutex_unlock(td->_lock); // 有可能执行else语句不会被执行
    }
    printf("%s...quit\n", name);
    return nullptr;
}

int main()
{
    // pthread_mutex_t lock;
    // pthread_mutex_init(&lock,nullptr);

    vector<pthread_t> tids;
    vector<threadData*> thread_datas;
    for(int i = 1; i <= NUM; ++i)
    {
        pthread_t tid;
        // threadData* td = new threadData(i, &lock);
        threadData* td = new threadData(i);
        thread_datas.push_back(td);
        pthread_create(&tid, nullptr, getTicket, thread_datas[i-1]);
        tids.push_back(tid);
    }

    for(auto thread : tids)
    {
        pthread_join(thread, nullptr);
    }

    for(auto td : thread_datas)
    {
        delete td;
    }

    // pthread_mutex_destroy(&lock);
    return 0;
}